/**
* \file	AbstractSyntaxTree.cpp
* \author Caio Arce Nishibe (caionishibe@gmail.com)
* \date	August, 2014
* \brief Abstract Syntax Tree Implementation.
*/

#include "AbstractSyntaxTree.h"


void AbstractSyntaxTree::_destroyAST(ASTNode *node)
{
	if (node != NULL)
	{
		_destroyAST(node->children);
		_destroyAST(node->sibling);
		delete node;
	}
}

void AbstractSyntaxTree::_printAST(ASTNode *node, int numTabs)
{
	if (node == NULL)
		return;

	string tabs;

	for (int i = 0; i < numTabs; i++)
		tabs = tabs + "  ";

	cout << tabs << "(" << numTabs << ")" << "(ASTNode) " << node << ": " << node->name << endl;

	string kind;

	switch (node->kind)
	{
	case PROGRAM:
		kind = "program";
		break;
	case NAME:
		kind = "name";
		break;
	case DECL:
		kind = "decl";
		break;
	case RULES:
		kind = "rules";
		break;
	case RULE:
		kind = "rule";
		break;
	case RULE_SET:
		kind = "rule_set";
		break;
	case CONDITION:
		kind = "condition";
		break;
	case INTERNAL_CONDITION:
		kind = "internal_condition";
		break;
	case ACTION:
		kind = "action";
		break;
	case COMMANDS:
		kind = "commands";
		break;
	case CMD:
		kind = "cmd";
		break;
	case ARITH_EXPR:
		kind = "arith_expr";
		break;
	case IDENTIFIER:
		kind = "identifier";
		break;
	case NUMBER:
		kind = "number";
		break;
	case COMP_OP:
		kind = "com_op";
		break;
	case LOGIC_OP:
		kind = "logic_op";
		break;
	case ASSIGNMENT:
		kind = "assignment";
		break;
	case READ_SENSOR:
		kind = "read_sensor";
		break;
	case WRITE_ACTUATOR:
		kind = "write_actuator";
		break;
	default:
		kind = "none";
		break;
	}

	cout << tabs << " |- kind: " << kind << endl;

	string type;

	switch (node->type)
	{
	case VOID:
		type = "void";
		break;
	case DOUBLE:
		type = "double";
		break;
	case STRING:
		type = "string";
		break;
	case COMPARATOR:
		type = "comparator";
		break;
	case LOGIC:
		type = "logic_operator";
		break;
	case ARITHMETIC:
		type = "arithmetic_operator";
		break;
	default:
		type = "error";
		break;
	}

	cout << tabs << " |- type: " << type << endl;
	cout << tabs << " |- line: " << node->lineNumber << endl;

	if (node->type == DOUBLE)
	{
		double number = 0;
		node->value.GetValue(DOUBLE, &number);
		cout << tabs << " |- value: " << number << endl;
	}
	else if (node->type == STRING)
	{
		string s;
		node->value.GetValue(STRING, &s);
		cout << tabs << " |- value: " << s << endl;
	}
	else if (node->type == COMPARATOR)
	{
		CompOp comparador;
		node->value.GetValue(COMPARATOR, &comparador);
		string sComp;
		switch (comparador)
		{
		case LESS:
			sComp = "<";
			break;
		case LESS_EQUAL:
			sComp = "<=";
			break;
		case GREATER:
			sComp = ">";
			break;
		case GREATER_EQUAL:
			sComp = ">=";
			break;
		case EQUAL:
			sComp = "==";
			break;
		case DIFFERENT:
			sComp = "<>";
			break;
		}

		cout << tabs << " |- value: " << sComp << endl;
	}
	else if (node->type == LOGIC)
	{
		LogicOp opL;
		node->value.GetValue(LOGIC, &opL);
		string sL;
		switch (opL)
		{
		case NOT:
			sL = "NOT";
			break;
		case AND:
			sL = "AND";
			break;
		case OR:
			sL = "OR";
			break;
		}

		cout << tabs << " |- value: " << sL << endl;
	}
	else if (node->type == ARITHMETIC)
	{
		ArithOp op;
		node->value.GetValue(ARITHMETIC, &op);
		string sO;

		switch (op)
		{
		case SUM:
			sO = "+";
			break;
		case SUBTRACTION:
			sO = "-";
			break;
		case MULTIPLICATION:
			sO = "*";
			break;
		case DIVISION:
			sO = "/";
			break;
		}

		cout << tabs << " |- value: " << sO << endl;
	}

	if (node->children != NULL)
	{
		_printAST(node->children, numTabs + 1);

	}

	if (node->sibling != NULL)
	{
		_printAST(node->sibling, numTabs);
	}


}

AbstractSyntaxTree::AbstractSyntaxTree()
{
	this->ast = NULL;
}

AbstractSyntaxTree::AbstractSyntaxTree(ASTNode *ast)
{
	this->ast = ast;
}

AbstractSyntaxTree::~AbstractSyntaxTree()
{
	_destroyAST(this->ast);
}

void AbstractSyntaxTree::PrintAST()
{
	_printAST(this->ast, 0);
}



